<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://at.alicdn.com/t/font_2790765_pqm82v7ytg.css">
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        img {
            vertical-align: top;
        }

        li {
            list-style: none;
        }

        .wrap {
            width: 1226px;
            margin: 50px auto;
            position: relative;
            overflow: hidden;
        }

        .nav {
            display: flex;
            line-height: 40px;
            text-align: center;
            position: absolute;
            right: 50px;
            bottom: 20px;
            z-index: 9999;
        }

        .nav li {
            width: 10px;
            height: 10px;
            border: 1px solid #333;
            border-radius: 50%;
            margin: 0 5px;
        }

        .active {
            background-color: #6ff;
            color: #fff;
        }

        .swiper {
            overflow: hidden;
            position: relative;
        }

        .swiper .swiper-item {
            float: left;
        }

        .swiper .swiper-item img {
            width: 100%;
        }

        .wrap .prev,
        .wrap .next {
            width: 50px;
            height: 80px;
            text-align: center;
            line-height: 80px;
            background-color: rgba(0, 0, 0, 0.3);
            font-size: 50px;
            color: #fff;
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            z-index: 9999;
            opacity: 0;
            transition: all 0.5s;
        }

        .wrap:hover .prev,
        .wrap:hover .next {
            opacity: 1;
        }

        .wrap .prev {
            left: 10px;
        }

        .wrap .next {
            right: 10px;
        }
    </style>
</head>

<body>
    <div class="wrap mySwiper">
        <i class="prev iconfont icon-left"></i>
        <i class="next iconfont icon-right"></i>
        <!-- 是否需要导航 -->
        <ul class="nav">
            <!-- <li class="active"></li>
            <li></li>
            <li></li>
            <li></li> -->
        </ul>
        <div class="swiper clear">
            <div class="swiper-item"><img src="../images/1.webp" alt=""></div>
            <div class="swiper-item"><img src="../images/2.webp" alt=""></div>
            <!-- <div class="swiper-item"><img src="../images/3.jpg" alt=""></div>
            <div class="swiper-item"><img src="../images/4.webp" alt=""></div>
            <div class="swiper-item"><img src="../images/1.webp" alt=""></div>
            <div class="swiper-item"><img src="../images/2.webp" alt=""></div>
            <div class="swiper-item"><img src="../images/3.jpg" alt=""></div>
            <div class="swiper-item"><img src="../images/4.webp" alt=""></div> -->
            <!-- <div class="swiper-item"><img src="../images/1.webp" alt=""></div> -->
        </div>
    </div>
</body>
<script>
    // 面向对象  
    // 思想上的提升 ->   就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与行为(方法)


    // 轮播 = {
    //     点击轮播,
    //     自动轮播,
    //     移入暂停,移出启动,
    //     左右点击切换
    // }

    // 字面量对象 -> 将面向过程中的变量和函数改为对象中的属性和方法
    var swiperObj = {
        // 先获取 轮播的容器 -> wrap
        wrap: document.querySelector(".mySwiper"),
        // 在通过wrap获取其内的子元素
        // nav: swiperObj.wrap.getElementsByClassName("nav")[0],
        // swiper: swiperObj.wrap.getElementsByClassName("swiper")[0],
        // liList: swiperObj.nav.getElementsByTagName("li"),
        // swiperItems: swiperObj.swiper.getElementsByClassName("swiper-item"),
        // prev: swiperObj.wrap.getElementsByClassName("prev")[0],
        // next: swiperObj.wrap.getElementsByClassName("next")[0],
        // swiperWidth: swiperObj.wrap.clientWidth,
        index: 0,
        timer: null,

        // 将获取元素相关的操作整合到一个方法中
        init: function () {
            this.getEle();  //先获取元素
            this.autoComplete();  //先获取元素
            this.bindEvent(); // 绑定事件 
            this.autoPlay(); // 自动轮播
        },
        getEle: function () {
            // this ->  swiperObj
            this.nav = this.wrap.getElementsByClassName("nav")[0];
            this.swiper = this.wrap.getElementsByClassName("swiper")[0];
            this.liList = this.nav.getElementsByTagName("li");
            this.swiperItems = this.swiper.getElementsByClassName("swiper-item");
            this.prev = this.wrap.getElementsByClassName("prev")[0];
            this.next = this.wrap.getElementsByClassName("next")[0];
            this.swiperWidth = this.wrap.clientWidth;
        },
        autoComplete: function () {
            // 动态生成

            // 1. 生成对应图片个数的li放到nav中  
            for (var i = 0; i < this.swiperItems.length; i++) {
                var li = document.createElement("li");
                if (i == this.index) { // i==0
                    li.classList.add("active");
                }
                this.nav.appendChild(li);
            }

            // 2. swiper中的第一个li 克隆 放到最后面
            var cloneFirst = this.swiper.firstElementChild.cloneNode(true);
            this.swiper.appendChild(cloneFirst);

            // swiper的宽度 =  (n+1) * wrap的宽度   => 四张图片  (4+1)*100%
            // this.swiperItems 传统方法获取元素 -> 自动更新
            this.swiper.style.width = this.swiperItems.length * this.swiperWidth + "px";

            // 单个swiper-item的宽度 = 100 / (n-1) + "%" =>   100 / (4+1) => 20%
            for (var i = 0; i < this.swiperItems.length; i++) {
                var swiperItem = this.swiperItems[i];
                swiperItem.style.width = (100 / this.swiperItems.length) + "%";
            }
        },
        // 绑定事件
        bindEvent: function () {
            // this -> swiperObj
            this.click();
            this.hover();
            this.switch();
        },
        click: function () {   // swiperObj.click
            // debugger;
            // this -> swiperObj
            var _this = this;
            for (let i = 0; i < this.liList.length; i++) {  // i=> 0-5
                let li = this.liList[i];  //获取对应下标的元素
                li.onclick = function () { // this-> 被点击的li
                    // 点击时记录下标  => 下次从点击位置开始轮播
                    _this.index = i;

                    _this.step();

                }
            }
        },
        hover: function () {// swiperObj
            // this -> swiperObj
            this.wrap.onmouseenter = () => {
                clearInterval(this.timer);
            }

            this.wrap.onmouseleave = () => {
                this.autoPlay();
            }

        },
        switch: function () {

            this.prev.onclick = () => {

                this.index--;  // 0 1 2 3  

                this.step();

            }
            this.next.onclick = () => {
                this.index++;  // 0 1 2 3  
                // console.log(index);
                this.step();

            }
        },
        autoPlay: function (delay = 3000) {  // swiperObj.autoPlay()
            // this -> swiperObj
            this.timer = setInterval(() => {
                this.index++;
                this.step();

            }, delay)
        },
        step: function () {

            if (this.index < 0) {  // 超出左边的临界值
                this.index = this.swiperItems.length - 1; // 5-1 => 4  (第五张下标)
                this.swiper.style.left = -(this.swiperWidth * this.index) + "px";  //切换到第五张

                this.index--;
            }

            if (this.index > this.swiperItems.length - 1) { // 如果超出第五张 (index > 4) 
                // 瞬间切换到第一张 
                this.swiper.style.left = 0;
                this.index = 0;  //第一张下标   

                this.index++;  // 下标切到第二张 => 后续会执行animate()  滚动到第二张

            }

            for (var j = 0; j < this.liList.length; j++) {
                this.liList[j].className = ""; // 清除每一个导航的class名
            }
            var activeIndex = this.index == this.swiperItems.length - 1 ? 0 : this.index;
            this.liList[activeIndex].className = "active";

            this.animate(this.swiper, { left: -(this.swiperWidth * this.index) }, "slow", () => {
                // 每次滚动结束时 会执行的回调函数
                // console.log("index", index);

                // 每次滚动结束时 判断是否滚动到第五张(下标: 4) 
                if (this.index >= this.swiperItems.length - 1) { // index >= 4 (滚动第五张)
                    this.swiper.style.left = 0;
                    this.index = 0;
                }

            })
        },
        animate: function (ele, params, type = "normal", callback) {
            // debugger;
            // callback 形参 -> 接收实际参数(本次有点特殊 -> 接收的是一个函数) 


            /* 获取非行内样式 */
            function getStyle(ele, cssKey) {
                if (window.getComputedStyle) {
                    return window.getComputedStyle(ele)[cssKey];
                } else {
                    return ele.currentStyle[cssKey];
                }
            }


            // 记录单属性运动的 cur end speed
            // var cur = ele.offsetLeft;  // 1. 记录小球的起始位置(left)  2. 每次运动后小球的位置
            // var cur = parseFloat(getStyle(ele, cssKey));  // 1. 记录小球的起始位置(left)  2. 每次运动后小球的位置
            // end = parseFloat(end); //去单位 ("1000px" => 1000)
            // var speed = 0;

            var cur = {};   // {left:当前值,top:当前值}
            var end = {};   // {left:终点,top:终点}  单位处理
            var speed = {}  // {left:速度,top:速度}  记录速度

            for (var cssKey in params) {  // csskey => 运动的css属性
                cur[cssKey] = parseFloat(getStyle(ele, cssKey));
                end[cssKey] = parseFloat(params[cssKey]);
                speed[cssKey] = 0;
            }
            // console.log("起始位置", cur);
            // console.log("终点", end);
            // console.log("速度", speed);

            // ele => 运动的元素(元素节点 -> 对象)  -> 元素节点的内置属性(className,id) 和 对象自定义属性
            // var data = {};
            // data.a = 1;
            // data.b = 2;

            // 将计时器的编号 作为元素的属性 存储到元素上(每个运动的元素都有一个timer属性 => 计时器的编号)
            // ele.timer = 1;

            clearInterval(ele.timer);
            ele.timer = setInterval(function () {

                // 多属性同时运动
                var isFinished = true;  // 假设都到达终点

                // {left:"1000px",top:500}
                for (var cssKey in params) {  // csskey => 运动的css属性  "left"  "top"

                    if (type == "normal") {
                        // 匀速运动
                        if (end[cssKey] > cur[cssKey]) {
                            speed[cssKey] = 20
                        } else if (end[cssKey] < cur[cssKey]) {
                            speed[cssKey] = -20;
                        } else {
                            speed[cssKey] = 0;
                        }

                    } else if (type == "fast") {
                        // 加速运动
                        if (end[cssKey] > cur[cssKey]) {  // 正向运动(0-1000   => speed正值)  
                            speed[cssKey] += 2;
                        } else if (end[cssKey] < cur[cssKey]) { // 反向运动(1000 - 0   => speed负值) 
                            speed[cssKey] -= 2;
                        } else {
                            speed[cssKey] = 0;
                        }

                    } else if (type == "slow") {
                        // 缓冲运动
                        // (终点值 - 当前位置)  => 剩余的距离
                        // 剩余的距离 / 缓冲因子(n) => (每次剩余的n分之1)

                        // 缓冲因子 => 一般在8-12之间

                        if (end[cssKey] > cur[cssKey]) {
                            speed[cssKey] = (end[cssKey] - cur[cssKey]) / 10;  // speed >0 =>  可能会出现 0.00001
                            speed[cssKey] = Math.ceil(speed[cssKey]);  //  如果速度小于1  取  1
                        } else if (end[cssKey] < cur[cssKey]) {
                            speed[cssKey] = (end[cssKey] - cur[cssKey]) / 10;  // speed < 0 =>  可能会出现-0.00001
                            speed[cssKey] = Math.floor(speed[cssKey]);  //  如果速度大于-1  取  -1
                        } else {
                            speed[cssKey] = 0;
                        }

                    }

                    // console.log(speed);
                    cur[cssKey] += speed[cssKey];  // 每次运动后小球的位置
                    ele.style[cssKey] = cur[cssKey] + "px";

                    if (Math.abs(cur[cssKey] - end[cssKey]) <= Math.abs(speed[cssKey])) {  // 到达终点后停下
                        ele.style[cssKey] = end[cssKey] + "px";  // 直接到达终点
                        delete params[cssKey];   //如果当前属性到达终点 => 移出该属性 (不会再遍历该属性)
                        // clearInterval(timer);   // 有一个到达终点就停下  
                    } else {  // 有一个没到达终点   => 推翻假设
                        isFinished = false;
                    }


                }
                // console.log(isFinished);
                if (isFinished) {   ///全都到达终点(本次运动结束)=> 清除计时器
                    clearInterval(ele.timer);
                    if (callback && typeof callback == "function") {  //如果callback有值 且 是函数
                        callback(); //执行该函数  -> 每次运动结束之后执行函数
                    }
                }

            }, 10);
        }

    }

    swiperObj.init();




    // window.obj.say();



</script>

</html>